home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / stevi69s.zip / CMDLINE.C < prev    next >
Text File  |  1990-04-23  |  13KB  |  660 lines

  1. /* $Header: /nw/tony/src/stevie/src/RCS/cmdline.c,v 1.20 89/08/13 11:41:23 tony Exp $
  2.  *
  3.  * Routines to parse and execute "command line" commands, such as searches
  4.  * or colon commands.
  5.  */
  6.  
  7. #include "stevie.h"
  8.  
  9. static    char    *altfile = NULL;    /* alternate file */
  10. static    int    altline;        /* line # in alternate file */
  11.  
  12. static    char    *nowrtmsg = "No write since last change (use ! to override)";
  13. static    char    *nooutfile = "No output file";
  14. static    char    *morefiles = "more files to edit";
  15.  
  16. extern    char    **files;        /* used for "n" and "rew" */
  17. extern    int    numfiles, curfile;
  18.  
  19. #define    CMDSZ    100        /* size of the command buffer */
  20.  
  21. static    void    get_range();
  22. static    LPTR    *get_line();
  23.  
  24. #ifdef __TURBOC__        /* v1.1 local prototypes */
  25. void doxit(void);
  26. #endif
  27.  
  28. /*
  29.  * getcmdln() - read a command line from the terminal
  30.  *
  31.  * Reads a command line started by typing '/', '?', '!', or ':'. Returns a
  32.  * pointer to the string that was read. For searches, an optional trailing
  33.  * '/' or '?' is removed.
  34.  */
  35. char *
  36. getcmdln(firstc)
  37. char    firstc;
  38. {
  39.     static    char    buff[CMDSZ];
  40.     register char    *p = buff;
  41.     register int    c;
  42.     register char    *q;
  43.  
  44.     gotocmd(TRUE, firstc);
  45.  
  46.     /* collect the command string, handling '\b' and @ */
  47.     do {
  48.         switch (c = vgetc()) {
  49.  
  50.         default:        /* a normal character */
  51.             outchar(c);
  52.             *p++ = c;
  53.             break;
  54.  
  55.         case BS:
  56.             if (p > buff) {
  57.                 /*
  58.                  * this is gross, but it relies
  59.                  * only on 'gotocmd'
  60.                  */
  61.                 p--;
  62.                 gotocmd(TRUE, firstc);
  63.                 for (q = buff; q < p ;q++)
  64.                     outchar(*q);
  65.             } else {
  66.                 msg("");
  67.                 return NULL;        /* back to cmd mode */
  68.             }
  69.             break;
  70.  
  71.         case '@':            /* line kill */
  72.             p = buff;
  73.             gotocmd(TRUE, firstc);
  74.             break;
  75.  
  76.         case ESC:            /* abandon command */
  77.             msg("");
  78.             return  NULL;
  79. /* v1.1        break; */
  80.  
  81.         case NL:            /* done reading the line */
  82.         case CR:
  83.             break;
  84.         }
  85.     } while (c != NL && c != CR);
  86.  
  87.     *p = '\0';
  88.  
  89.     if (firstc == '/' || firstc == '?') {    /* did we do a search? */
  90.         /*
  91.          * Look for a terminating '/' or '?'. This will be the first
  92.          * one that isn't quoted. Truncate the search string there.
  93.          */
  94.         for (p = buff; *p ;) {
  95.             if (*p == firstc) {    /* we're done */
  96.                 *p = '\0';
  97.                 break;
  98.             } else if (*p == '\\')    /* next char quoted */
  99.                 p += 2;
  100.             else
  101.                 p++;        /* normal char */
  102.         }
  103.     }
  104.     return buff;
  105. }
  106.  
  107. /*
  108.  * docmdln() - handle a colon command
  109.  *
  110.  * Handles a colon command received interactively by getcmdln() or from
  111.  * the environment variable "EXINIT" (or eventually .virc).
  112.  */
  113. void
  114. docmdln(cmdline)
  115. char    *cmdline;
  116. {
  117.     char    buff[CMDSZ];
  118.     char    cmdbuf[CMDSZ];
  119.     char    argbuf[CMDSZ];
  120.     char    *cmd, *arg;
  121.     register char    *p;
  122.     /*
  123.      * The next two variables contain the bounds of any range given in a
  124.      * command. If no range was given, both contain null line pointers.
  125.      * If only a single line was given, u_pos will contain a null line
  126.      * pointer.
  127.      */
  128.     LPTR    l_pos, u_pos;
  129.  
  130.  
  131.     /*
  132.      * Clear the range variables.
  133.      */
  134.     l_pos.linep = (struct line *) NULL;
  135.     u_pos.linep = (struct line *) NULL;
  136.  
  137.     if (cmdline == NULL)
  138.         return;
  139.  
  140.     if (strlen(cmdline) > CMDSZ-2) {
  141.         msg("Error: command line too long");
  142.         return;
  143.     }
  144.     strcpy(buff, cmdline);
  145.  
  146.     /* skip any initial white space */
  147.     for (cmd = buff; *cmd != NUL && isspace(*cmd) ;cmd++)
  148.         ;
  149.  
  150.     if (*cmd == '%') {        /* change '%' to "1,$" */
  151.         strcpy(cmdbuf, "1,$");    /* kind of gross... */
  152.         strcat(cmdbuf, cmd+1);
  153.         strcpy(cmd, cmdbuf);
  154.     }
  155.  
  156.     while ((p=strchr(cmd, '%')) != NULL && *(p-1) != '\\') {
  157.                     /* change '%' to Filename */
  158.         if (Filename == NULL) {
  159.             emsg("No filename");
  160.             return;
  161.         }
  162.         *p= NUL;
  163.         strcpy (cmdbuf, cmd);
  164.         strcat (cmdbuf, Filename);
  165.         strcat (cmdbuf, p+1);
  166.         strcpy(cmd, cmdbuf);
  167.         msg(cmd);            /*repeat */
  168.     }
  169.  
  170.     while ((p=strchr(cmd, '#')) != NULL && *(p-1) != '\\') {
  171.                     /* change '#' to Altname */
  172.         if (altfile == NULL) {
  173.             emsg("No alternate file");
  174.             return;
  175.         }
  176.         *p= NUL;
  177.         strcpy (cmdbuf, cmd);
  178.         strcat (cmdbuf, altfile);
  179.         strcat (cmdbuf, p+1);
  180.         strcpy(cmd, cmdbuf);
  181.         msg(cmd);            /*repeat */
  182.     }
  183.  
  184.     /*
  185.      * Parse a range, if present (and update the cmd pointer).
  186.      */
  187.     get_range(&cmd, &l_pos, &u_pos);
  188.  
  189.     if (l_pos.linep != NULL) {
  190.         if (LINEOF(&l_pos) > LINEOF(&u_pos)) {
  191.             emsg("Invalid range");
  192.             return;
  193.         }
  194.     }
  195.  
  196.     strcpy(cmdbuf, cmd);    /* save the unmodified command */
  197.  
  198.     /* isolate the command and find any argument */
  199.     for ( p=cmd; *p != NUL && ! isspace(*p); p++ )
  200.         ;
  201.     if ( *p == NUL )
  202.         arg = NULL;
  203.     else {
  204.         *p = NUL;
  205.         for (p++; *p != NUL && isspace(*p) ;p++)
  206.             ;
  207.         if (*p == NUL)
  208.             arg = NULL;
  209.         else {
  210.             strcpy(argbuf, p);
  211.             arg = argbuf;
  212.         }
  213.     }
  214.     if (strcmp(cmd,"q!") == 0)
  215.         getout();
  216.     if (strcmp(cmd,"q") == 0) {
  217.         if (Changed)
  218.             emsg(nowrtmsg);
  219.         else {
  220.             if ((curfile + 1) < numfiles)
  221.                 emsg(morefiles);
  222.             else
  223.                 getout();
  224.         }
  225.         return;
  226.     }
  227.     if (strcmp(cmd,"w") == 0) {
  228.         if (arg == NULL) {
  229.             if (Filename != NULL) {
  230.                 writeit(Filename, &l_pos, &u_pos);
  231.             } else
  232.                 emsg(nooutfile);
  233.         }
  234.         else
  235.             writeit(arg, &l_pos, &u_pos);
  236.         return;
  237.     }
  238.     if (strcmp(cmd,"wq") == 0) {
  239.         if (Filename != NULL) {
  240.             if (writeit(Filename, (LPTR *)NULL, (LPTR *)NULL))
  241.                 getout();
  242.         } else
  243.             emsg(nooutfile);
  244.         return;
  245.     }
  246.     if (strcmp(cmd, "x") == 0) {
  247.         doxit();
  248.         return;
  249.     }
  250.  
  251.     if (strcmp(cmd,"f") == 0 && arg == NULL) {
  252.         fileinfo();
  253.         return;
  254.     }
  255.     if (*cmd == 'n') {
  256.         if ((curfile + 1) < numfiles) {
  257.             /*
  258.              * stuff ":e[!] FILE\n"
  259.              */
  260.             stuffin(":e");
  261.             if (cmd[1] == '!')
  262.                 stuffin("!");
  263.             stuffin(" ");
  264.             stuffin(files[++curfile]);
  265.             stuffin("\n");
  266.         } else
  267.             emsg("No more files!");
  268.         return;
  269.     }
  270.     if (*cmd == 'N') {
  271.         if (curfile > 0) {
  272.             /*
  273.              * stuff ":e[!] FILE\n"
  274.              */
  275.             stuffin(":e");
  276.             if (cmd[1] == '!')
  277.                 stuffin("!");
  278.             stuffin(" ");
  279.             stuffin(files[--curfile]);
  280.             stuffin("\n");
  281.         } else
  282.             emsg("No more files!");
  283.         return;
  284.     }
  285.     if (strncmp(cmd, "rew", 3) == 0) {
  286.         if (numfiles <= 1)        /* nothing to rewind */
  287.             return;
  288.         curfile = 0;
  289.         /*
  290.          * stuff ":e[!] FILE\n"
  291.          */
  292.         stuffin(":e");
  293.         if (cmd[3] == '!')
  294.             stuffin("!");
  295.         stuffin(" ");
  296.         stuffin(files[0]);
  297.         stuffin("\n");
  298.         return;
  299.     }
  300.     if (strcmp(cmd,"e") == 0 || strcmp(cmd,"e!") == 0) {
  301.         (void) doecmd(arg, cmd[1] == '!');
  302.         return;
  303.     }
  304.     /*
  305.      * The command ":e#" gets expanded to something like ":efile", so
  306.      * detect that case here.
  307.      */
  308.     if (*cmd == 'e' && arg == NULL) {
  309.         if (cmd[1] == '!')
  310.             (void) doecmd(&cmd[2], TRUE);
  311.         else
  312.             (void) doecmd(&cmd[1], FALSE);
  313.         return;
  314.     }
  315.     if (strcmp(cmd,"f") == 0) {
  316.         EnvEval (arg, CMDSZ);    /* expand environment vars */
  317.         Filename = strsave(arg);
  318.         filemess("");
  319.         return;
  320.     }
  321.     if (strcmp(cmd,"r") == 0) {
  322.         if (arg == NULL) {
  323.             badcmd();
  324.             return;
  325.         }
  326.         if (readfile(arg, Curschar, 1)) {
  327.             emsg("Can't open file");
  328.             return;
  329.         }
  330.         updatescreen();
  331.         CHANGED;
  332.         return;
  333.     }
  334.     if (strcmp(cmd,"=") == 0) {
  335.         smsg("%d", cntllines(Filemem, &l_pos));
  336.         return;
  337.     }
  338.     if (strncmp(cmd,"ta", 2) == 0) {
  339.         dotag(arg, cmd[2] == '!');
  340.         return;
  341.     }
  342.     if (strncmp(cmd,"untag", 5) == 0) {
  343.         if (P(P_TG))
  344.             dountag(cmd[5]);
  345.         else
  346.             emsg("Tag stacking not enabled");
  347.         return;
  348.     }
  349.     if (strncmp(cmd,"set", 2) == 0) {
  350.         doset(arg);
  351.         return;
  352.     }
  353.     if (strcmp(cmd,"help") == 0) {
  354.         if (help()) {
  355.             screenclear();
  356.             updatescreen();
  357.         }
  358.         return;
  359.     }
  360.     if (strncmp(cmd, "ve", 2) == 0) {
  361.         extern    char    *Version;
  362.  
  363.         msg(Version);
  364.         return;
  365.     }
  366.     if (strcmp(cmd, "sh") == 0) {
  367.         doshell(NULL);
  368.         return;
  369.     }
  370.     if (*cmd == '!') {
  371.         doshell(cmdbuf+1);
  372.         return;
  373.     }
  374.     if (strncmp(cmd, "s/", 2) == 0) {
  375.         dosub(&l_pos, &u_pos, cmdbuf+1);
  376.         return;
  377.     }
  378.     if (strncmp(cmd, "g/", 2) == 0) {
  379.         doglob(&l_pos, &u_pos, cmdbuf+1);
  380.         return;
  381.     }
  382.     /*
  383.      * If we got a line, but no command, then go to the line.
  384.      */
  385.     if (*cmd == NUL && l_pos.linep != NULL) {
  386.         *Curschar = l_pos;
  387.         return;
  388.     }
  389.  
  390.     badcmd();
  391. }
  392.  
  393.  
  394. void doxit(void)    /* v1.1 */
  395. {
  396.     if (Changed) {
  397.         if (Filename != NULL) {
  398.             if (!writeit(Filename, (LPTR *)NULL, (LPTR *)NULL))
  399.                 return;
  400.         } else {
  401.             emsg(nooutfile);
  402.             return;
  403.         }
  404.     }
  405.     if ((curfile + 1) < numfiles)
  406.         emsg(morefiles);
  407.     else
  408.         getout();
  409. }
  410.  
  411. /*
  412.  * get_range - parse a range specifier
  413.  *
  414.  * Ranges are of the form:
  415.  *
  416.  * addr[,addr]
  417.  *
  418.  * where 'addr' is:
  419.  *
  420.  * $  [+- NUM]
  421.  * 'x [+- NUM]    (where x denotes a currently defined mark)
  422.  * .  [+- NUM]
  423.  * NUM
  424.  *
  425.  * The pointer *cp is updated to point to the first character following
  426.  * the range spec. If an initial address is found, but no second, the
  427.  * upper bound is equal to the lower.
  428.  */
  429. static void
  430. get_range(cp, lower, upper)
  431. register char    **cp;
  432. LPTR    *lower, *upper;
  433. {
  434.     register LPTR    *l;
  435.     register char    *p;
  436.  
  437.     if ((l = get_line(cp)) == NULL)
  438.         return;
  439.  
  440.     *lower = *l;
  441.  
  442.     for (p = *cp; *p != NUL && isspace(*p) ;p++)
  443.         ;
  444.  
  445.     *cp = p;
  446.  
  447.     if (*p != ',') {        /* is there another line spec ? */
  448.         *upper = *lower;
  449.         return;
  450.     }
  451.  
  452.     *cp = ++p;
  453.  
  454.     if ((l = get_line(cp)) == NULL) {
  455.         *upper = *lower;
  456.         return;
  457.     }
  458.  
  459.     *upper = *l;
  460. }
  461.  
  462. static LPTR *
  463. get_line(cp)
  464. char    **cp;
  465. {
  466.     static    LPTR    pos;
  467.     LPTR    *lp;
  468.     register char    *p, c;
  469.     register int    lnum;
  470.  
  471.     pos.index = 0;        /* shouldn't matter... check back later */
  472.  
  473.     p = *cp;
  474.     /*
  475.      * Determine the basic form, if present.
  476.      */
  477.     switch (c = *p++) {
  478.  
  479.     case '$':
  480.         pos.linep = Fileend->linep->prev;
  481.         break;
  482.  
  483.     case '.':
  484.         pos.linep = Curschar->linep;
  485.         break;
  486.  
  487.     case '\'':
  488.         if ((lp = getmark(*p++)) == NULL) {
  489.             emsg("Unknown mark");
  490.             return (LPTR *) NULL;
  491.         }
  492.         pos = *lp;
  493.         break;
  494.  
  495.     case '0': case '1': case '2': case '3': case '4':
  496.     case '5': case '6': case '7': case '8': case '9':
  497.         for (lnum = c - '0'; isdigit(*p) ;p++)
  498.             lnum = (lnum * 10) + (*p - '0');
  499.  
  500.         pos = *gotoline(lnum);
  501.         break;
  502.  
  503.     default:
  504.         return (LPTR *) NULL;
  505.     }
  506.  
  507.     while (*p != NUL && isspace(*p))
  508.         p++;
  509.  
  510.     if (*p == '-' || *p == '+') {
  511.         bool_t    neg = (*p++ == '-');
  512.  
  513.         for (lnum = 0; isdigit(*p) ;p++)
  514.             lnum = (lnum * 10) + (*p - '0');
  515.  
  516.         if (neg)
  517.             lnum = -lnum;
  518.  
  519.         pos = *gotoline( cntllines(Filemem, &pos) + lnum );
  520.     }
  521.  
  522.     *cp = p;
  523.     return &pos;
  524. }
  525.  
  526. void
  527. badcmd()
  528. {
  529.     emsg("Unrecognized command");
  530. }
  531.  
  532. bool_t
  533. doecmd(arg, force)
  534. char    *arg;
  535. bool_t    force;
  536. {
  537.     int    line = 1;        /* line # to go to in new file */
  538.  
  539.     if (!force && Changed) {
  540.         emsg(nowrtmsg);
  541.         if (altfile)
  542.             free(altfile);
  543.         altfile = strsave(arg);
  544.         return FALSE;
  545.     }
  546.     if (arg != NULL) {
  547.         /*
  548.          * First detect a ":e" on the current file. This is mainly
  549.          * for ":ta" commands where the destination is within the
  550.          * current file.
  551.          */
  552.         if (Filename != NULL && strcmp(arg, Filename) == 0) {
  553.             if (!Changed || (Changed && !force))
  554.                 return TRUE;
  555.         }
  556.         if (altfile) {
  557.             if (strcmp (arg, altfile) == 0)
  558.                 line = altline;
  559.             free(altfile);
  560.         }
  561.         altfile = Filename;
  562.         altline = cntllines(Filemem, Curschar);
  563.         Filename = strsave(arg);
  564.     }
  565.     if (Filename == NULL) {
  566.         emsg("No filename");
  567.         return FALSE;
  568.     }
  569.  
  570.     /* clear mem and read file */
  571.     freeall();
  572.     filealloc();
  573.     UNCHANGED;
  574.  
  575.     if (readfile(Filename, Filemem, 0))
  576.         filemess("[New File]");
  577.  
  578.     *Topchar = *Curschar;
  579.     if (line != 1) {
  580.         stuffnum(line);
  581.         stuffin("G");
  582.     }
  583.     do_mlines();
  584.     setpcmark();
  585.     updatescreen();
  586.     return TRUE;
  587. }
  588.  
  589. void
  590. gotocmd(clr, firstc)
  591. bool_t  clr;
  592. char    firstc;
  593. {
  594.     windgoto(Rows-1,0);
  595.     if (clr)
  596.         CLEOL;        /* clear the bottom line */
  597.     if (firstc)
  598.         outchar(firstc);
  599. }
  600.  
  601. /*
  602.  * msg(s) - displays the string 's' on the status line
  603.  */
  604. void
  605. msg(s)
  606. char    *s;
  607. {
  608.     gotocmd(TRUE, 0);
  609.     outstr(s);
  610.     flushbuf();
  611. }
  612.  
  613. /*VARARGS1*/
  614. void
  615. smsg(s, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16)
  616. char    *s;
  617. int    a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16;
  618. {
  619.     char    sbuf[80];
  620.  
  621.     sprintf(sbuf, s,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16);
  622.     msg(sbuf);
  623. }
  624.  
  625. /*
  626.  * emsg() - display an error message
  627.  *
  628.  * Rings the bell, if appropriate, and calls message() to do the real work
  629.  */
  630. void
  631. emsg(s)
  632. char    *s;
  633. {
  634.     if (P(P_EB))
  635.         beep();
  636.     msg(s);
  637. }
  638.  
  639. void
  640. wait_return()
  641. {
  642.     register char    c;
  643.  
  644.     if (got_int)
  645.         outstr("Interrupt: ");
  646.  
  647.     outstr("Press RETURN to continue");
  648.     do {
  649.         c = vgetc();
  650.     } while (c != CR && c != NL && c != ' ' && c != ':');
  651.  
  652.     if (c == ':') {
  653.         outchar(NL);
  654.         docmdln(getcmdln(c));
  655.     } else
  656.         screenclear();
  657.  
  658.     updatescreen();
  659. }
  660.